// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-12-08

package managers

import (
	"context"
	"gitee.com/go-libs/log"
	"gitee.com/go-libs/process"
	"gitee.com/gomq/sr"
	"gomq/app/models"
	"sync"
)

var singleton *manager

type (
	// Manager is a component for package managers.
	Manager interface {
		// MemoryManager return the memory manager instance.
		MemoryManager() (mm MemoryManager)

		// Process return the virtual process like os.
		Process() (p process.Process)

		// Supporter return the supporter manager for sr.Scheduler and
		// sr.Servlet and sr.Transform
		Supporter() (supporter sr.Supporter)

		// ServerManager return the server manager instance.
		ServerManager() (sm ServerManager, has bool)
	}

	manager struct {
		loading   bool
		mm        MemoryManager
		mu        *sync.RWMutex
		name      string
		process   process.Process
		sm        ServerManager
		supporter sr.Supporter
	}
)

// Singleton return the singleton instance of the root manager.
func Singleton() Manager { return singleton }

// + Implements

func (o *manager) MemoryManager() MemoryManager         { return o.mm }
func (o *manager) Process() process.Process             { return o.process }
func (o *manager) ServerManager() (ServerManager, bool) { return o.getServerManager() }
func (o *manager) Supporter() sr.Supporter              { return o.supporter }

// + Process

func (o *manager) After(_ context.Context) (err error) {
	// Detach memory updated handler.
	o.mm.Detach(o.name)

	// Wait server manager stopped.
	if sm := o.sm; sm != nil {
		sm.Process().StopWait()
	}
	return
}

func (o *manager) Before(_ context.Context) (err error) {
	o.mm.Attach(o.name, o.loader)
	return
}

func (o *manager) Run(ctx context.Context) (err error) {
	// Load memory data when root manager start.
	if err = o.mm.Load(ctx); err != nil {
		return
	}

	// Listen context channel message until context cancelled.
	for {
		select {
		case <-ctx.Done():
			return
		}
	}
}

// + Reload.

func (o *manager) loader(_ context.Context) (err error) {
	o.mu.Lock()

	// Skip if previous loader not done.
	if loading := o.loading; loading {
		o.mu.Unlock()
		return
	}

	// Lock loader state to prevent repeat to do.
	sm := o.sm
	o.loading = true
	o.mu.Unlock()

	// Unlock loader state when done.
	defer func() {
		o.mu.Lock()
		o.loading = false
		o.mu.Unlock()
	}()

	// Read enabled cluster model from memory.
	var cluster *models.Cluster
	if cluster, err = o.mm.GetCluster(); err != nil {
		log.Warnf(`[%s] %v`, o.name, err)

		// Wait previous server manager stopped if load active cluster
		// failed.
		if sm != nil {
			o.mu.Lock()
			o.sm = nil
			o.mu.Unlock()

			log.Infof(`[%s] stop previous server manager`, o.name)
			sm.Process().StopWait()
		}
		return
	}

	// Loader process.
	return o.loaderCluster(cluster, sm)
}

func (o *manager) loaderCluster(cluster *models.Cluster, sm ServerManager) (err error) {
	var servlet sr.Servlet

	// Server manager was existed already.
	if sm != nil {
		if sm.Keep(cluster) {
			sm.Flush()
			return
		}

		// Stop previous server manager and wait stopped in order to
		// restart a new instance.
		o.mu.Lock()
		o.sm = nil
		o.mu.Unlock()

		log.Infof(`[%s] stop previous server manager`, o.name)
		sm.Process().StopWait()
	}

	// Create servlet instance.
	if servlet, err = o.supporter.NewServlet(cluster.Plugin); err != nil {
		log.Errorf(`[%s] %v, cluster-id="%d", plugin="%s"`, o.name, err, cluster.Id, cluster.Plugin)
		return
	}

	// Config servlet instance.
	if err = servlet.Configure([]byte(cluster.Configuration)); err != nil {
		log.Errorf(`[%s] %v`, o.name, err)
		return
	}

	// Create a new server manager and replace reference.
	log.Infof(`[%s] create server manager: cluster-id="%d", cluster-plugin="%s", cluster-title="%s"`, o.name, cluster.Id, cluster.Plugin, cluster.Title)
	sm = (&server{cluster: cluster, manager: o, servlet: servlet}).init()
	o.mu.Lock()
	o.sm = sm
	o.mu.Unlock()

	// Add server name as a child process and start immediately.
	o.process.Add(sm.Process())
	return
}

// + Access

func (o *manager) getServerManager() (sm ServerManager, has bool) {
	o.mu.RLock()
	defer o.mu.RUnlock()
	if sm = o.sm; sm != nil {
		has = true
	}
	return
}

// + Constructor

func (o *manager) init() *manager {
	o.name = "root-manager"
	o.mu = new(sync.RWMutex)
	o.initProcess()
	o.initSupporter()
	return o
}

func (o *manager) initProcess() {
	o.mm = (&memory{manager: o}).init()
	o.process = process.New(o.name, o)
	o.process.Add(o.mm.Process().Prevent(true))
}

func (o *manager) initSupporter() {
	o.supporter = sr.SupporterSingleton()
}
